using System;
using System.Collections.Generic;
using System.Text;
using C5;

namespace CoreCalc.SheetDefinedFunctions
{
  /// <summary>
  /// An instance of this class maps each function sheet cell to the
  /// names of sheet-defined functions using that cell, and conversely,
  /// maps each name of a sheet-defined function to the cells that 
  /// function uses.  There should be a single instance, as a static field 
  /// in class SdfManager.
  /// </summary>
  public class CellsUsedInFunctions
  {
    public readonly HashDictionary<FullCellAddr, HashSet<string>> addressToFunctionList;
    private readonly HashDictionary<string, List<FullCellAddr>> functionToAddressList;
    public readonly HashBag<FullCellAddr> inputCellBag, outputCellBag;

    public CellsUsedInFunctions()
    {
      addressToFunctionList = new HashDictionary<FullCellAddr, HashSet<string>>();
      functionToAddressList = new HashDictionary<string, List<FullCellAddr>>();
      inputCellBag = new HashBag<FullCellAddr>();
      outputCellBag = new HashBag<FullCellAddr>();
    }

    internal C5.ICollection<string> GetFunctionsUsingAddresses(IEnumerable<FullCellAddr> fcas)
    {
      C5.ICollection<string> affectedFunctions = new TreeSet<string>();
      foreach (FullCellAddr addr in fcas)
      {
        HashSet<string> names;
        if (addressToFunctionList.Find(addr, out names))
          affectedFunctions.AddAll(names);
      }
      return affectedFunctions;
    }

    internal void AddFunction(SdfInfo info, C5.ICollection<FullCellAddr> addrs)
    {
      HashSet<FullCellAddr> inputCellSet = new HashSet<FullCellAddr>();
      inputCellSet.AddAll(info.inputCells);
      foreach (FullCellAddr addr in addrs)
        if (!inputCellSet.Contains(addr))
          AddCellToFunction(info.name, addr);
      List<FullCellAddr> addrsList = new List<FullCellAddr>(addrs);
      functionToAddressList.UpdateOrAdd(info.name, addrsList);
      inputCellBag.AddAll(info.inputCells);
      outputCellBag.Add(info.outputCell);
    }

    internal void RemoveFunction(SdfInfo info)
    {
      inputCellBag.RemoveAll(info.inputCells);
      outputCellBag.Remove(info.outputCell);
      List<FullCellAddr> addresses;
      if (!functionToAddressList.Find(info.name, out addresses))
        return;
      foreach (FullCellAddr addr in addresses)
        addressToFunctionList.Remove(addr);
      functionToAddressList.Remove(info.name);
    }

    private void AddCellToFunction(String info, FullCellAddr addr)
    {
      HashSet<String> names;
      if (!addressToFunctionList.Find(addr, out names))
        names = new HashSet<String>();
      names.Add(info);
      addressToFunctionList.UpdateOrAdd(addr, names);
    }
  }
}
